package uml.eventsystem;

import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;

import uml.GUI.UMLContextMenu;
import uml.eventsystem.events.UMLEvent;
import uml.eventsystem.events.UMLEventId;
import uml.util.UMLConstants;

public class MouseHandler
		implements MouseListener, MouseMotionListener, ISubject
{
	private static final UMLEventId[] m_allEvents = { UMLEventId.SELECT, UMLEventId.MULTI_SELECT, UMLEventId.TRANSLATE };
	private static MouseHandler m_mouseHandler;
	private SubjectImpl m_subjectImplementation;
	private Point m_pressedPoint;
	private UMLContextMenu m_contextMenu;
	public static Point m_dp = new Point ();

	/**
	 * This constructor only gets called once during the program to ensure there
	 * is only one instance of this class
	 */
	private MouseHandler ()
	{
		m_subjectImplementation = new SubjectImpl (this);
		m_contextMenu = new UMLContextMenu ();
		setUpContextMenu ();
	}

	/**
	 * populates Context Menu
	 */
	private void setUpContextMenu ()
	{
		//addMenuItem ("Delete", UMLConstants.DELETE_STROKE, UMLEventId.DELETE);
	}

	//	private void addMenuItem(String name, UMLEventId event)
	//	{
	//		JMenuItem item = new JMenuItem(name);
	//		item.addActionListener(m_contextMenu);
	//		m_contextMenu.put(name, event);
	//		m_contextMenu.add(item);
	//	}

	/**
	 * Adds menuItems to the context menu
	 * 
	 * @param name
	 * @param stroke
	 * @param event
	 */
	private void addMenuItem (String name, KeyStroke stroke, UMLEventId event)
	{
		JMenuItem item = new JMenuItem (name);
		item.addActionListener (m_contextMenu);
		item.setAccelerator (stroke);
		m_contextMenu.put (name, event);
		m_contextMenu.add (item);
	}

	/**
	 * If the <tt>MouseHandler</tt> has not been created yet creates it and then
	 * return the <tt>MouseHandler</tt> otherwise just returns the created
	 * <tt>MouseHandler</tt>
	 * 
	 * @return
	 */
	public static MouseHandler getMouseHandler ()
	{
		if (m_mouseHandler == null)
			m_mouseHandler = new MouseHandler ();
		return m_mouseHandler;
	}

	/**
	 * Does nothing
	 */
	public void mouseEntered (MouseEvent event)
	{
		//System.out.println("Mouse entered: " + event);
	}

	/**
	 * Only does something when the user double clicks. Upon a double click the
	 * object that the user clicked on requests focus.
	 */
	public void mouseClicked (MouseEvent event)
	{
		setContextMenuVisible (false);
		if (event.getButton () == MouseEvent.BUTTON3)
		{
			toggleContextMenu ();
			m_contextMenu.setLocation (event.getLocationOnScreen ());
		}
	}

	/**
	 * toggles visibility of contextMenu
	 */
	public void toggleContextMenu ()
	{
		m_contextMenu.setVisible (!m_contextMenu.isVisible ());
	}

	/**
	 * sets visibility of contextMenu
	 * 
	 * @param visible
	 */
	public void setContextMenuVisible (boolean visible)
	{
		m_contextMenu.setVisible (visible);
	}

	/**
	 * Does nothing
	 */
	@Override
	public void mouseExited (MouseEvent event)
	{
	}

	/**
	 * If the user held down control sends out a <tt>UMLEventId</tt>
	 * MULTI_SELECT event otherwise sends out a <tt>UMLEventId</tt> SELECT
	 * event.
	 */
	@Override
	public void mousePressed (MouseEvent event)
	{
		//record location mouse was pressed
		m_pressedPoint = event.getLocationOnScreen ();
		//handle based on modifiers active during event
		if (event.isControlDown ())
			m_subjectImplementation.notifyObservers (new UMLEvent (UMLEventId.MULTI_SELECT, event.getSource ()));
		else
			m_subjectImplementation.notifyObservers (new UMLEvent (UMLEventId.SELECT, event.getSource ()));
	}

	/**
	 * Does nothing
	 */
	@Override
	public void mouseReleased (MouseEvent arg0)
	{
		// TODO Auto-generated method stub
	}

	/**
	 * adds observer to m_subjectImplementation
	 */
	@Override
	public void addObserver (IObserver observer, UMLEventId eventId)
	{
		m_subjectImplementation.addObserver (observer, eventId);
	}

	/**
	 * removes observer from m_subjectImplementation
	 */
	@Override
	public void removeObserver (IObserver observer, UMLEventId eventId)
	{
		m_subjectImplementation.removeObserver (observer, eventId);
	}

	/**
	 * notifies observers of an eventId
	 */
	@Override
	public void notifyObservers (UMLEventId eventId)
	{
		m_subjectImplementation.notifyObservers (eventId);
	}

	/**
	 * Records the distance the mouse was dragged since the last
	 * <tt>MouseEvent</tt> in m_dp. Then sends out a <tt>UMLEventId</tt>
	 * TRANSLATE.
	 */
	@Override
	public void mouseDragged (MouseEvent event)
	{
		m_dp.x = (int) (event.getXOnScreen () - m_pressedPoint.getX ());
		m_dp.y = (int) (event.getYOnScreen () - m_pressedPoint.getY ());
		m_pressedPoint = event.getLocationOnScreen ();
		notifyObservers (UMLEventId.TRANSLATE);
	}

	/**
	 * Does nothing
	 */
	@Override
	public void mouseMoved (MouseEvent event)
	{
		// TODO Auto-generated method stub
	}

	/**
	 * Registers the observer for all <tt>UMLEventId</tt>s this class uses.
	 * 
	 * @param observer
	 */
	public void registerForAllUMLEvents (IObserver observer)
	{
		m_contextMenu.registerEvents (observer);
		for (UMLEventId eventId : m_allEvents)
			addObserver (observer, eventId);
	}
}
